﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;

namespace Ones.Infrastructure.Utils.Pagination.Impl
{
    [DataContract]
    public class LazyPagination<T> : IPagination<T> where T : class
    {
        #region Member Variables
        
        public const int DefaultPageSize = 10;
        private IList<T> results;
        private int totalItems;
        
        #endregion
        
        #region Constructor
        
        public LazyPagination(IQueryable<T> query, int pageNumber, int pageSize)
        {
            PageNumber = pageNumber;
            PageSize = pageSize;
            Query = query;
        }
        
        #endregion
        
        #region Getter/Setter
        
        [DataMember]
        public IQueryable<T> Query { get; private set; }
        
        [DataMember]
        public int PageSize { get; private set; }
        
        [DataMember]
        public int PageNumber { get; private set; }
        
        [DataMember]
        public int TotalItems
        {
            get
            {
                TryExecuteQuery();
                return totalItems;
            }
            private set
            {
            }
        }
        
        [DataMember]
        public int TotalPages
        {
            get
            {
                return (int)Math.Ceiling(((double)TotalItems) / PageSize);
            }
            private set
            {
            }
        }
        
        [DataMember]
        public int FirstItem
        {
            get
            {
                TryExecuteQuery();
                return ((PageNumber - 1) * PageSize) + 1;
            }
            private set
            {
            }
        }
        
        [DataMember]
        public int LastItem
        {
            get
            {
                return FirstItem + results.Count - 1;
            }
            private set
            {
            }
        }
        
        [DataMember]
        public bool HasPreviousPage
        {
            get
            {
                return PageNumber > 1;
            }
            private set
            {
            }
        }
        
        [DataMember]
        public bool HasNextPage
        {
            get
            {
                return PageNumber < TotalPages;
            }
            private set
            {
            }
        }
        
        #endregion
        
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            TryExecuteQuery();

            //var lst = new List<T>();////code them

            foreach (var item in results)
            {
                yield return item;////code goc
                //lst.Add(item);//code them
            }
            //return (IEnumerator<T>)lst;//code them
        }
        
        protected void TryExecuteQuery()
        {
            //Results is not null, means query has already been executed.
            if (results != null)
            {
                return;
            }
            
            totalItems = Query.Count();
            results = ExecuteQuery();
        }
        
        /// <summary>
        /// Calls Queryable.Skip/Take to perform the pagination.
        /// </summary>
        /// <returns>The paged set of results.</returns>
        protected virtual IList<T> ExecuteQuery()
        {
            int numberToSkip = (PageNumber - 1) * PageSize;
            return Query.Skip(numberToSkip).Take(PageSize).ToList();
        }
        
        public IEnumerator GetEnumerator()
        {
            return ((IEnumerable<T>)this).GetEnumerator();
        }
    }
}